package com.idea.relax.exception.core.manager;

import com.idea.relax.exception.api.ICodeSegment;
import com.idea.relax.exception.api.IErrorCode;
import com.idea.relax.exception.toolkit.BusinessExceptionHelper;
import com.idea.relax.exception.viewer.NodeModel;
import com.idea.relax.exception.viewer.ErrorCodeModel;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;

import java.lang.reflect.Method;
import java.util.*;

/**
 * @className: ErrorCodeManager
 * @description: 异常码的核心管理器
 * @author: salad
 * @date: 2023/5/24
 **/
public class ErrorCodeManager {

	/**
	 * 维护异常码枚举实例对象和异常码的映射
	 */
	private static final Map<ErrorCodeEnumInstance, String> CODE_ENUM_INSTANCE_POOL = Collections.synchronizedMap(new LinkedHashMap<>(16));

	/**
	 * IErrorCode
	 */
	private static final Class<?> ERROR_CODE_CLASS = IErrorCode.class;

	private static final Comparator<NodeModel> NODE_MODEL_COMPARATOR = Comparator.comparing(NodeModel::getCode);

	private static final Comparator<ErrorCodeModel> ERROR_MODEL_COMPARATOR = Comparator.comparing(ErrorCodeModel::getCode);


	private static MessageSource messageSource;

	public static void setMessageSource(MessageSource messageSource) {
		ErrorCodeManager.messageSource = messageSource;
	}

	public static String getMessageSourceErrorCode(IErrorCode errorCode) {
		if (messageSource != null) {
			return messageSource.getMessage(errorCode.getErrorCode(), null, LocaleContextHolder.getLocale());
		}
		return errorCode.getMessage();
	}

	/**
	 * 注册异常码
	 *
	 * @param errorCodes 异常码枚举类集合
	 */
	public static void register(List<IErrorCode> errorCodes) {
		for (IErrorCode errorCode : errorCodes) {
			ErrorCodeManager.register(errorCode);
		}
	}

	/**
	 * 注册异常码
	 *
	 * @param errorCode 异常码枚举类
	 */
	public static void register(IErrorCode errorCode) {
		ErrorCodeEnumInstance errorCodeEnumInstance;
		if (!(errorCode instanceof ErrorCodeEnumInstance)) {
			errorCodeEnumInstance = convertToCodeEnumInstance(errorCode);
		} else {
			errorCodeEnumInstance = (ErrorCodeEnumInstance) errorCode;
		}
		validatePresent(errorCodeEnumInstance);
		CODE_ENUM_INSTANCE_POOL.put(errorCodeEnumInstance, buildErrorCode(errorCode));
	}

	/**
	 * 获取异常码
	 *
	 * @param errorCode 异常码枚举类
	 * @return 异常码
	 */
	public static String getErrorCode(IErrorCode errorCode) {
		if (errorCode == null) {
			return null;
		}
		ErrorCodeEnumInstance errorCodeEnumInstance;
		if ((errorCode instanceof ErrorCodeEnumInstance)) {
			errorCodeEnumInstance = (ErrorCodeEnumInstance) errorCode;
		} else {
			errorCodeEnumInstance = convertToCodeEnumInstance(errorCode);
		}
		return CODE_ENUM_INSTANCE_POOL.get(errorCodeEnumInstance);
	}


	/**
	 * 将IErrorCode枚举类转换为ErrorCodeEnumInstance实例
	 *
	 * @param errorCode 异常码枚举类
	 * @return ErrorCodeEnumInstance实例
	 */
	public static ErrorCodeEnumInstance convertToCodeEnumInstance(IErrorCode errorCode) {
		if (!errorCode.getClass().isEnum()) {
			throw new RuntimeException("IErrorCode子类只能是ErrorCodeEnumInstance类型或者枚举类型");
		}
		ErrorCodeEnumInstance errorCodeEnumInstance = new ErrorCodeEnumInstance();
		errorCodeEnumInstance.setCode(errorCode.getCode());
		errorCodeEnumInstance.setLeftCodeSegment(errorCode.leftCodeSegment());
		errorCodeEnumInstance.setErrorCode(errorCode.getErrorCode());
		errorCodeEnumInstance.setUserTip(errorCode.getUserTip());
		errorCodeEnumInstance.setMessage(errorCode.getMessage());
		errorCodeEnumInstance.setEnumName(((Enum<?>) errorCode).name());
		errorCodeEnumInstance.setClassName(errorCode.getClass().getSimpleName());
		return errorCodeEnumInstance;
	}

	/**
	 * 验证异常码是否重复
	 *
	 * @param errorCodeEnumInstance 存储异常码枚举类信息
	 */
	public static void validatePresent(ErrorCodeEnumInstance errorCodeEnumInstance) {
		if (CODE_ENUM_INSTANCE_POOL.get(errorCodeEnumInstance) != null) {
			throw new RuntimeException("该异常码已经注册，请不要重复注册:[" + CODE_ENUM_INSTANCE_POOL.get(errorCodeEnumInstance) + "]");
		}
	}

	/**
	 * 构建异常码
	 *
	 * @param errorCode 异常码枚举类
	 * @return 异常码
	 */
	public static String buildErrorCode(IErrorCode errorCode) {
		if (errorCode.leftCodeSegment() == null) {
			return errorCode.getErrorCode();
		}
		List<String> codes = new ArrayList<>();
		codes.add(errorCode.getErrorCode());
		resolveCodeSegment(codes, errorCode.leftCodeSegment());
		StringBuilder stringBuilder = new StringBuilder();
		for (int i = codes.size() - 1; i >= 0; i--) {
			stringBuilder.append(codes.get(i));
		}
		return stringBuilder.toString();
	}

	/**
	 * 解析CodeSegment,将其异常码封装为一个集合
	 *
	 * @param codes       异常码集合
	 * @param codeSegment 异常码CodeSegment
	 */
	private static void resolveCodeSegment(List<String> codes, ICodeSegment codeSegment) {
		if (codeSegment == null) {
			return;
		}
		if (codeSegment.getCode() == null) {
			throw new RuntimeException("ICodeSegment:[" + codeSegment.getClass().getSimpleName() + "]#getCode()方法返回值不能为空");
		}
		codes.add(codeSegment.getCode());
		resolveCodeSegment(codes, codeSegment.leftCodeSegment());
	}


	/**
	 * 获取列表形式的异常码集合
	 *
	 * @return 异常码信息集合
	 */
	public static List<ErrorCodeModel> getCodeList() {
		List<ErrorCodeModel> errorCodeModels = new ArrayList<>();
		CODE_ENUM_INSTANCE_POOL.forEach((k, v) -> {
			ErrorCodeModel errorCodeModel = new ErrorCodeModel(v, k.getMessage(), k.getUserTip(), k.getEnumName(), k.getClassName());
			errorCodeModels.add(errorCodeModel);
		});
		errorCodeModels.sort(ERROR_MODEL_COMPARATOR);
		return errorCodeModels;
	}

	/**
	 * 获取树状结构的异常码集合
	 *
	 * @return 异常码信息集合
	 */
	public static Set<NodeModel> getCodeTree() {
		Set<NodeModel> rootNodes = new TreeSet<>(NODE_MODEL_COMPARATOR);
		Map<NodeModel, Set<NodeModel>> subNodes = new LinkedHashMap<>();
		CODE_ENUM_INSTANCE_POOL.forEach((k, v) -> {
			ErrorCodeModel errorCodeModel = new ErrorCodeModel(v, k.getMessage(), k.getUserTip(), k.getEnumName(), k.getClassName());
			parseTree(0, rootNodes, subNodes, k.leftCodeSegment(), errorCodeModel);
		});
		subNodeTree(rootNodes, subNodes);
		return rootNodes;
	}

	/**
	 * 将Class<Enum>解析为ErrorCodeEnumInstance集合
	 *
	 * @param clazz
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings({"rawtypes"})
	public static List<ErrorCodeEnumInstance> getCodeEnumInstance(Class<Enum> clazz) {
		if (!ERROR_CODE_CLASS.isAssignableFrom(clazz)) {
			throw new RuntimeException("Class必须为" + ERROR_CODE_CLASS.getName() + "的子类");
		}
		// 得到枚举类对象
		List<ErrorCodeEnumInstance> errorCodeEnumInstances = new ArrayList<>();
		try {
			//获取所有枚举实例
			Enum[] enumConstants = clazz.getEnumConstants();
			//根据方法名获取方法
			Method codeMethod = clazz.getMethod("getCode");
			Method leftCodeSegmentMethod = clazz.getMethod("leftCodeSegment");
			Method errorCodeMethod = clazz.getMethod("getErrorCode");
			Method userTipMethod = clazz.getMethod("getUserTip");
			Method messageMethod = clazz.getMethod("getMessage");
			for (Enum e : enumConstants) {
				//执行枚举方法获得枚举实例对应的值
				ErrorCodeEnumInstance errorCodeEnumInstance = new ErrorCodeEnumInstance();
				errorCodeEnumInstance.setCode((Integer) codeMethod.invoke(e));
				errorCodeEnumInstance.setMessage((String) messageMethod.invoke(e));
				errorCodeEnumInstance.setErrorCode((String) errorCodeMethod.invoke(e));
				ICodeSegment codeSegment = (ICodeSegment) leftCodeSegmentMethod.invoke(e);
				if (null != codeSegment) {
					CodeSegmentEnumInstance enumInstance = new CodeSegmentEnumInstance();
					enumInstance.setCode(codeSegment.getCode());
					enumInstance.setDesc(codeSegment.getDesc());
					convertToCodeSegmentEnumInstance(enumInstance, codeSegment);
					errorCodeEnumInstance.setLeftCodeSegment(enumInstance);
				}
				errorCodeEnumInstance.setUserTip((String) userTipMethod.invoke(e));
				errorCodeEnumInstance.setEnumName(e.name());
				errorCodeEnumInstance.setClassName(e.getClass().getName());
				errorCodeEnumInstances.add(errorCodeEnumInstance);
			}
		} catch (Exception e) {
			throw new RuntimeException("将枚举Class解析为ErrorCodeEnumInstance集合失败", e);
		}
		return errorCodeEnumInstances;
	}

	/**
	 * 将CodeSegment枚举类转换为CodeSegmentEnumInstance实例
	 *
	 * @param instance
	 * @param codeSegment
	 */
	private static void convertToCodeSegmentEnumInstance(CodeSegmentEnumInstance instance, ICodeSegment codeSegment) {
		if (codeSegment == null) {
			return;
		}
		if (codeSegment.leftCodeSegment() != null) {
			CodeSegmentEnumInstance parent = new CodeSegmentEnumInstance();
			parent.setCode(codeSegment.leftCodeSegment().getCode());
			parent.setDesc(codeSegment.leftCodeSegment().getDesc());
			instance.setLeftCodeSegment(parent);
			convertToCodeSegmentEnumInstance((CodeSegmentEnumInstance) instance.leftCodeSegment(), codeSegment.leftCodeSegment());
		}
	}

	/**
	 * 解析异常码为树状结构
	 *
	 * @param rootNodes
	 * @param subNodes
	 * @param codeSegment
	 * @param errorCodeModel
	 */
	private static void parseTree(int level, Set<NodeModel> rootNodes, Map<NodeModel, Set<NodeModel>> subNodes,
								  ICodeSegment codeSegment, ErrorCodeModel errorCodeModel) {
		level++;
		//一级父级都没有，表示是单层级的异常码
		if (codeSegment == null) {
			return;
		}
		NodeModel model = new NodeModel();
		model.setCode(codeSegment.getCode());
		model.setDesc(codeSegment.getDesc());
		if (level == 1) {
			Set<ErrorCodeModel> set = new TreeSet<>(ERROR_MODEL_COMPARATOR);
			set.add(errorCodeModel);
			model.setErrorCodeModels(set);
		}
		//如果是null，表示改为父节点
		if (codeSegment.leftCodeSegment() == null) {
			rootNodes.add(model);
		} else {
			//如果不是null，表示还存在父级节点 1 2 3, 2 2 3    3 [2]  2,[2,1]
			ICodeSegment parent = codeSegment.leftCodeSegment();
			Set<NodeModel> childNode = subNodes.get(new NodeModel(parent.getCode(), parent.getDesc()));
			if (childNode == null) {
				childNode = new TreeSet<>(NODE_MODEL_COMPARATOR);
				childNode.add(model);
				subNodes.put(new NodeModel(parent.getCode(), parent.getDesc()), childNode);
			}
			if (childNode.contains(model)) {
				childNode.stream().filter(model::equals).forEach(item -> {
					if (item.getErrorCodeModels() != null) {
						item.getErrorCodeModels().addAll(model.getErrorCodeModels());
					} else {
						if (model.getErrorCodeModels() != null) {
							Set<ErrorCodeModel> set = new TreeSet<>(ERROR_MODEL_COMPARATOR);
							set.addAll(model.getErrorCodeModels());
							item.setErrorCodeModels(set);
						}
					}
				});
			}
			childNode.add(model);
			parseTree(level, rootNodes, subNodes, parent, errorCodeModel);
		}

	}

	/**
	 * 设置子节点
	 *
	 * @param rootNodes
	 * @param subNodes
	 */
	private static void subNodeTree(Set<NodeModel> rootNodes, Map<NodeModel, Set<NodeModel>> subNodes) {
		for (NodeModel rootNode : rootNodes) {
			setChildNode(rootNode, subNodes);
		}
	}

	/**
	 * 设置子节点
	 *
	 * @param rootNode
	 * @param subNodes
	 */
	private static void setChildNode(NodeModel rootNode, Map<NodeModel, Set<NodeModel>> subNodes) {
		Set<NodeModel> childNode = subNodes.get(rootNode);
		if (childNode == null) {
			return;
		}
		rootNode.setNodeModels(childNode);
		for (NodeModel nodeModel : childNode) {
			setChildNode(nodeModel, subNodes);
		}
	}

}
